
#include "direct_matrix.hpp"

#include <aftermath/numerics/matrix.hpp>
#include <aftermath/numerics/ssse_matrix.hpp>
#include <aftermath/numerics/dsse_matrix.hpp>

#include <chrono>
//#include <conio.h>
#include <cstdint>
#include <iostream>
#include <string>

template <int M, int N, typename T, typename S>
static std::ostream& operator <<(std::ostream& os, const aftermath::numerics::matrix<M, N, T, S>& value)
{
    std::cout << std::endl << value.get_element(0, 0);
    for (int j = 1; j < N; j++) std::cout << ", " << value.get_element(0, j);
    for (int i = 1; i < M; i++)
    {
        std::cout << std::endl << value.get_element(i, 0);
        for (int j = 1; j < N; j++) std::cout << ", " << value.get_element(i, j);
    }
    return os;
}

template <int M, int N, int K, typename T, typename S = aftermath::numerics::default_sse_for<T>::type>
struct test
{
    static void multiplication()
    {
        aftermath::numerics::matrix<M, N, T, S> left; // uninitialized
        aftermath::numerics::matrix<N, K, T, S> right; // uninitialized
        left.fill(1.0);
        right.fill(2.0);

        aftermath::numerics::matrix<M, K, T, S> product = left.multiply(right);

        /** Smth. that never happens, but fools the compiler not to optimize product out. */
        if (rand() < -1) std::cout << product.get_element(0, 0) << std::endl;
    }
};

template <typename F>
void benchmark(F func)
{
    typedef std::chrono::high_resolution_clock clock_type;
    auto tic = clock_type::now();
    func();
    auto toc = clock_type::now();
    auto dt = toc.time_since_epoch() - tic.time_since_epoch();

    std::cout << "Elapsed time " << static_cast<double>(dt.count() * clock_type::period::num) / clock_type::period::den << " s." << std::endl;
}

int main() // (int argc, char* argv[], char* envp[])
{
	// WARNING: this is MS-specific!
	
    std::cout << "float (sse):     ";
    benchmark([] () { test<1024, 512, 2048, float>::multiplication(); });
    
    std::cout << "double (sse):    ";
    benchmark([] () { test<1024, 512, 2048, double>::multiplication(); });
    
    std::cout << "float (no sse):  ";
    benchmark([] () { test<1024, 512, 2048, float, void>::multiplication(); });
    
    std::cout << "double (no sse): ";
    benchmark([] () { test<1024, 512, 2048, float, void>::multiplication(); });
        
    //std::cout << "Press any key to continue . . ." << std::endl;
    return 0;
}




